/*
This allows us to handle Entities within the game much more easily, 
rather than clumping them together with everything else in the game within the main class. 
*/
#include "stdafx.h"
#include "GEntity.h"
#include "GEntityCol.h"

//This vector will hold all of our entities, 
//easily accessible through GEntity::EntityList, because it's declared as a static.
std::vector<GEntity*> GEntity::EntityList;

GEntity GEntity::EntityControl;

GEntity::GEntity()
{
	Surf_Entity = NULL;

	X = Y = 0.0f;

	Width = Height = 0;

	AnimState = 0;

	MoveLeft = MoveRight = MoveUp = MoveDown = false;

	Type = ENTITY_TYPE_GENERIC;

    Dead = false;

    Flags = ENTITY_FLAG_NONE;

    SpeedX = 0;
    SpeedY = 0;

    AccelX = 0;
    AccelY = 0;

    MaxSpeedX = 9;
    MaxSpeedY = 9;

    CurrentFrameCol = 0;
    CurrentFrameRow = 0;

    Col_X = 0;
    Col_Y = 0;

    Col_Width  = 0;
    Col_Height = 0;
}

GEntity::~GEntity()
{
}

bool GEntity::OnLoad(char* File, int Width, int Height, int MaxFrames)
{
	if((Surf_Entity = SDLSurface::OnLoad(File)) == NULL)
	{
		return false;
	}

	//By default, we have it setting a transparent color
	SDLSurface::Tranparent(Surf_Entity, 0, 0, 0);

	this->Width = Width;
	this->Height = Height;

	Anim_Control.MaxFrames = MaxFrames;
	Anim_Control.Oscillate = true;

	return true;
}

void GEntity::OnLoop()
{
	if(MoveLeft == false && MoveRight == false
		&& MoveUp == false && MoveDown == false)
	{
		StopMove();
    }

	if(MoveLeft) 
	{
        AccelX = -0.5;
    }

    if(MoveRight) 
	{
        AccelX = 0.5;
    }

	if(MoveUp) 
	{
        AccelY = -0.5;
    }

    if(MoveDown) 
	{
        AccelY = 0.5;
    }

	if(Flags & ENTITY_FLAG_GRAVITY) 
	{
        AccelY = 0.75f;
    }

	SpeedX += AccelX * GFPS::FPSControl.GetSpeedFactor();
    SpeedY += AccelY * GFPS::FPSControl.GetSpeedFactor();

	if(SpeedX > MaxSpeedX)  
		SpeedX =  MaxSpeedX;
    if(SpeedX < -MaxSpeedX) 
		SpeedX = -MaxSpeedX;
    if(SpeedY > MaxSpeedY)  
		SpeedY =  MaxSpeedY;
    if(SpeedY < -MaxSpeedY) 
		SpeedY = -MaxSpeedY;


	//Anim_Control.OnAnimate();
	OnAnimate();
	OnMove(SpeedX, SpeedY);
	EdgeDetection();
}

void GEntity::OnRender(SDL_Surface* Surf_Display)
{
	if(Surf_Entity == NULL || Surf_Display == NULL)
		return;

	SDLSurface::OnDraw(Surf_Display, Surf_Entity, 
		X - GCamera::CameraControl.GetX(), 
		Y - GCamera::CameraControl.GetY(),
		(CurrentFrameRow + Anim_Control.GetCurrentFrame()) * Width, 
		CurrentFrameCol * Height, 
		Width, Height);
}

void GEntity::OnCleanup()
{
	if(Surf_Entity)
	{
		SDL_FreeSurface(Surf_Entity);
	}
	Surf_Entity = NULL;
}

void GEntity::OnAnimate()
{
	if(MoveLeft) 
	{
        CurrentFrameCol = 1;
    }else

    if(MoveRight) 
	{
        CurrentFrameCol = 2;
    }
	if(MoveUp) 
	{
        CurrentFrameCol = 3;
    }else

    if(MoveDown) 
	{
        CurrentFrameCol = 0;
    }

    Anim_Control.OnAnimate();
}

bool GEntity::OnCollision(GEntity* Entity)
{
	return false;
}

//MoveX and MoveY are the where we want to move our entity
void GEntity::OnMove(float MoveX, float MoveY)
{
	if(MoveX == 0 && MoveY == 0)
		return;

	//These will be our X and Y increments
	double NewX = 0;
    double NewY = 0;

    MoveX *= GFPS::FPSControl.GetSpeedFactor();
    MoveY *= GFPS::FPSControl.GetSpeedFactor();

	if(MoveX != 0) {
        if(MoveX >= 0)
			NewX = GFPS::FPSControl.GetSpeedFactor();
        else           
			NewX = -GFPS::FPSControl.GetSpeedFactor();
    }

    if(MoveY != 0) {
        if(MoveY >= 0)   
			NewY = GFPS::FPSControl.GetSpeedFactor();
        else             
			NewY = -GFPS::FPSControl.GetSpeedFactor();
    }

	while(true)
	{
		if(Flags & ENTITY_FLAG_GHOST)
		{
			//We don't care about collisions, but we need to send events to other entities
			PosValid((int)(X + NewX + GCamera::CameraControl.GetTargetModeX()), 
					 (int)(Y + NewY + GCamera::CameraControl.GetTargetModeY()));
			X += NewX;
			Y += NewY;
		}
		else
		{
			if(PosValid((int)(X + NewX + GCamera::CameraControl.GetTargetModeX()), 
						(int)(Y + GCamera::CameraControl.GetTargetModeY())))
			{
				X += NewX;
			}
			else
			{
				SpeedX = 0;
			}
			if (PosValid((int)(X + GCamera::CameraControl.GetTargetModeX()), 
						 (int)(Y + NewY + GCamera::CameraControl.GetTargetModeY())))
			{
				Y += NewY;
			}
			else
			{
				SpeedY = 0;
			}
		}

		MoveX += -NewX;
		MoveY += -NewY;

		if(NewX > 0 && MoveX <= 0) 
			NewX = 0;
		if(NewX < 0 && MoveX >= 0) 
			NewX = 0;

		if(NewY > 0 && MoveY <= 0) 
			NewY = 0;
		if(NewY < 0 && MoveY >= 0) 
			NewY = 0;

		if(MoveX == 0) 
			NewX = 0;
		if(MoveY == 0) 
			NewY = 0;

		if(MoveX == 0 && MoveY == 0)   
			break;
		if(NewX == 0 && NewY == 0)   
			break;
	}
}

void GEntity::StopMove()
{
	if(SpeedX > 0) 
	{
		AccelX = -1;
	}

	if(SpeedX < 0) 
	{
		AccelX =  1;
	}

	if(SpeedY > 0)
	{
		AccelY = -1;
	}

	if (SpeedY < 0)
	{
		AccelY = 1;
	}

	if(SpeedX < 2.0f && SpeedX > -2.0f)
	{
		AccelX = 0;
		SpeedX = 0;
	}
	
	if(SpeedY < 2.0f && SpeedY > -2.0f)
	{
		AccelY = 0;
		SpeedY = 0;
	}
}

//Basically, we are doing bounding box collision
bool GEntity::Collides(int oX, int oY, int oW, int oH)
{
	int left1, left2;
	int right1, right2;
	int top1, top2;
	int bottom1, bottom2;

	int tX = (int)X + Col_X;
	int tY = (int)Y + Col_Y;

	left1 = tX;
	left2 = oX;

	right1 = left1 + Width - 1 - Col_Width;
	right2 = oX + oW - 1;

	top1 = tY;
	top2 = oY;

	bottom1 = top1 + Height - 1 - Col_Height;
	bottom2 = oY + oH - 1;

	if (bottom1 < top2) 
		return false;
	if (top1 > bottom2) 
		return false;

	if (right1 < left2) 
		return false;
	if (left1 > right2) 
		return false;

	return true;
}

void GEntity::EdgeDetection()
{
	//Set edge situation
	if((X >= 0 && Y >= 0)
		&& (X <= MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH 
		&& Y <= MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT))
	{
		GCamera::CameraControl.SetTarget(&X, &Y);
	}
	else
	{		
		if(X < 0 || Y < 0)
		{
			GCamera::CameraControl.SetPosToStart();

			if(X < 0 && Y < 0)
			{
				GCamera::CameraControl.SetTarget();
			}

			if(X >= 0 && 
				X <= MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH)
			{
				GCamera::CameraControl.SetTargetX(&X);
			}
			
			if(Y >= 0 &&
				Y <= MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT)
			{
				GCamera::CameraControl.SetTargetY(&Y);
			}
		}

		if(X > MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH
			|| Y > MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT)
		{
			GCamera::CameraControl.SetPosToEnd();

			if(X > MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH
				&& Y > MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT)
			{
				GCamera::CameraControl.SetTarget();
			}
			if(X >= 0 && 
				X <= MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH)
			{
				GCamera::CameraControl.SetTargetX(&X);
			}
			if(Y >= 0 &&
				Y <= MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT)
			{
				GCamera::CameraControl.SetTargetY(&Y);
			}
		}

		if(X > MAP_WIDTH * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WWIDTH
			&& Y < 0)
		{
			GCamera::CameraControl.SetPosToRight();

			GCamera::CameraControl.SetTarget();
		}

		if(X < 0 &&
			Y > MAP_HEIGHT * TILE_SIZE * GArea::AreaControl.GetAreaSize() - WHEIGHT)
		{
			GCamera::CameraControl.SetPosToLeft();

			GCamera::CameraControl.SetTarget();
		}
	}
}

//This function will determine any type of collision that has taken place
bool GEntity::PosValid(int NewX, int NewY)
{
	bool Return = true;

	//The two position of start and end point
	int StartX     = (NewX + Col_X) / TILE_SIZE;
	int StartY     = (NewY + Col_Y) / TILE_SIZE;

	int EndX    = ((NewX + Col_X) + Width - Col_Width - 1) / TILE_SIZE;
	int EndY    = ((NewY + Col_Y) + Height - Col_Height - 1) / TILE_SIZE;

	for(int iY = StartY;iY <= EndY;iY++) 
	{
		for(int iX = StartX;iX <= EndX;iX++)
		{
			/*
			GetTile will return the GTile of any X and Y coordinate. 
			After we get the tile, we pass it on to PosValidTile for it 
			to figure out if the tile can be moved over or not.
			*/
			GTile* Tile = GArea::AreaControl.GetTile(iX * TILE_SIZE, iY * TILE_SIZE);

			if(PosValidTile(Tile) == false) 
			{
				Return = false;
			}
		}
	}

	if(Flags & ENTITY_FLAG_MAPONLY) 
	{
		//If so, this means we're only checking collisions against the map, 
		//so we don't worry about Entity collisions
	}
	else
	{
		for(int i = 0;i < EntityList.size();i++)
		{
			if(PosValidEntity(EntityList[i], NewX, NewY) == false) 
			{
				Return = false;
			}
		}
	}

	return Return;
}

bool GEntity::PosValidTile(GTile* Tile)
{
	if (Tile == NULL)
	{
		return true;
	}

	if (Tile->TypeID == TILE_TYPE_BLOCK)
	{
		return false;
	}

	return true;
}

/*
We do some checks first on the Entity 
(the Entity we are wanting to collide with) 
to determine that this Entity is not the same Entity we're moving. 
That's the "this != Entity." It simply checks to make sure the Entities are not the same. 
We then check to make sure the Entity is not NULL, not Dead, 
and doesn't have the ENTITY_FLAG_MAPONLY flag turned on. 
So far, so good. If all these checks pass, then we finally pass it to the Collides
function of that Entity, passing along our Entities coordinates and Width/Height (with Col_ offsets). 
Remember, if any part of an if statement fails, like this != Entity, 
none of the other code in the if statement will be executed.
*/
//////////////////////////////////////////////////////////////////////////
/*
If the Entities do collide, finally, we use our new CEntityCol class. 
This will be a queue of collisions that have taken place. 
So, we push our Entity into EntityA, and the other Entity into EntityB, 
and then push it into the list. Finally, we return false, to indicate that this position is not valid.
*/
bool GEntity::PosValidEntity(GEntity* Entity, int NewX, int NewY) 
{
	if(this != Entity && Entity != NULL && Entity->Dead == false &&
		Entity->Flags ^ ENTITY_FLAG_MAPONLY &&
		Entity->Collides(NewX + Col_X, NewY + Col_Y, Width - Col_Width - 1, Height - Col_Height - 1) == true) 
	{

			GEntityCol EntityCol;

			EntityCol.EntityA = this;
			EntityCol.EntityB = Entity;

			GEntityCol::EntityColList.push_back(EntityCol);

			return false;
	}

	return true;
}

